#pragma once
#include "Common.h"
#include "ObjectPool.hpp"
#include "PageMap.h"

class PageCache
{
public:
	static PageCache* GetInstance()
	{
		return _pInst;
	}

	Span* MapObjToSpan(void* obj)
	{
		PAGE_ID id = (reinterpret_cast<PAGE_ID>(obj) >> PAGE_SHIFT);
		auto ret = _IdSpanMap.get(id);
		if (ret != nullptr)
		{
			return reinterpret_cast<Span*>(ret);
		}
		assert(false);
		return nullptr;
	}

	// 从PageCache桶中获取Span给CentralCache
	Span* NewSpan(size_t pageNum)
	{
		assert(pageNum > 0);
		if (pageNum > NPAGES - 1)
		{
			void* ptr = SystemAlloc(pageNum);
			Span* span = _spanPool.New();
			span->_pageid = (reinterpret_cast<PAGE_ID>(ptr) >> PAGE_SHIFT);
			span->_n = pageNum;
			span->_ObjSize = pageNum << PAGE_SHIFT;
			_IdSpanMap.set(span->_pageid,reinterpret_cast<void*>(span));
			return span;
		}
		else
		{
			// cout << std::this_thread::get_id() << ":" << "向页缓存获取Span" << endl;
			// 先检查对应桶是否有span
			if (!_spanLists[pageNum].Empty())
			{

				Span* span = _spanLists[pageNum].PopFront();
				for (PAGE_ID i = 0; i < (span->_n); ++i)
				{
					_IdSpanMap.set(span->_pageid + i,span);
				}
				return span;
			}

			// 到了这里说明对应桶无span，检查一下后面的桶有无span，有的话可以把它切分
			for (size_t i = pageNum + 1; i < NPAGES; ++i)
			{
				if (!_spanLists[i].Empty())
				{
					Span* nSpan = _spanLists[i].PopFront();
					// Span* kSpan = new Span;
					Span* kSpan = _spanPool.New();
					// 在nSpan头部切一块pageNum页的Span
					kSpan->_pageid = nSpan->_pageid;
					kSpan->_n = pageNum;
					nSpan->_pageid += pageNum;
					nSpan->_n -= pageNum;
					// 切完之后把nSpan挂到对应桶
					// 返回切的kSpan
					_spanLists[nSpan->_n].PushFront(nSpan);

					// 存储nSpan的首位页号跟nSpan映射，方便PageCache回收内存时进行合并查找
					_IdSpanMap.set(nSpan->_pageid, nSpan);
					_IdSpanMap.set(nSpan->_pageid + nSpan->_n - 1 ,nSpan);
					/*for (PAGE_ID i = 0; i < (nSpan->_n); ++i)
					{
						_IdSpanMap[nSpan->_pageid + i] = nSpan;
					}*/

					// 建立id和span的映射,方便CentralCache回收小块内存时,查找对应的Span
					for (PAGE_ID i = 0; i < (kSpan->_n); ++i)
					{
						_IdSpanMap.set(kSpan->_pageid + i, kSpan);
					}
					return kSpan;
				}
			}

			// 到了这里说明没有更大块的Span了，需要向堆申请大块内存
			// cout << std::this_thread::get_id() << ":" << "向系统堆获取大块内存" << endl;
			Span* bigSpan = _spanPool.New();
			void* ptr = SystemAlloc(NPAGES - 1);
			bigSpan->_pageid = reinterpret_cast<PAGE_ID>(ptr) >> PAGE_SHIFT; // PAGE_ID 是由地址位操作得来的
			bigSpan->_n = NPAGES - 1;

			// 这里一般需要执行上面相同代码切分成两块Span，但是直接写会代码冗余
			// 使用递归再次调用此函数让函数完成Span切分任务,避免代码冗余
			_spanLists[bigSpan->_n].PushFront(bigSpan);
			return NewSpan(pageNum);
		}
	}

	// 对span前后的页，进行合并，环节内存碎片的问题
	void ReleaseSpanToPageCache(Span* span)
	{
		// 超大块内存释放,直接调用系统调用释放
		if (span->_n > NPAGES - 1)
		{
			void* ptr = (void*)(span->_pageid << PAGE_SHIFT);
			SystemFree(ptr);
			_IdSpanMap.set(span->_pageid,nullptr); // 在映射表中删除
			// delete span;
			_spanPool.Delete(span);
			return;
		}
		// 向前合并
		while (1)
		{
			PAGE_ID prevID = span->_pageid - 1;
			auto it = _IdSpanMap.find(prevID);
			// 前面页号没有，不继续合并
			if (it == nullptr)
				break;
			// 前面相邻页的span在使用，不继续合并
			Span* prevSpan = reinterpret_cast<Span*>(it);
			if (prevSpan->_isUse == true)
				break;
			// 合并超过128页的span，无法管理，不继续合并
			if (prevSpan->_n + span->_n > (NPAGES-1))
				break;

			_IdSpanMap.erase(span->_pageid); // 在映射表中删除
			span->_pageid = prevSpan->_pageid;
			span->_n += prevSpan->_n;

			_spanLists[prevSpan->_n].Erase(prevSpan);
			_spanPool.Delete(prevSpan);
		}

		// 向后合并
		while (1)
		{
			PAGE_ID nextID = span->_pageid + span->_n;
			auto it = _IdSpanMap.find(nextID);
			// 后面页号没有，不继续合并
			if (it == nullptr)
				break;
			// 后面相邻页的span在使用，不继续合并
			Span* nextSpan = reinterpret_cast<Span*>(it);
			if (nextSpan->_isUse == true)
				break;
			// 合并超过128页的span，无法管理，不继续合并
			if (nextSpan->_n + span->_n > (NPAGES - 1))
				break;

			span->_n += nextSpan->_n;

			_IdSpanMap.erase(nextSpan->_pageid);
			_spanLists[nextSpan->_n].Erase(nextSpan);
			_spanPool.Delete(nextSpan);
		}

		_spanLists[span->_n].PushFront(span);
		span->_isUse = false;
		_IdSpanMap.set(span->_pageid , span);
		_IdSpanMap.set(span->_pageid + span->_n - 1 , span);
		// cout << "PageCache:归还合并好的大块内存:" << std::this_thread::get_id() << endl;
	}

public:
	std::mutex _pageMtx;

private:
	PageCache()
	{}

	PageCache(const PageCache&) = delete;

	ObjectPool<Span> _spanPool;
	SpanList _spanLists[NPAGES];
#ifdef _WIN64
	TCMalloc_PageMap3<64 - PAGE_SHIFT> _IdSpanMap; // 对SpanID 和 Span建立映射
#elif _WIN32
	TCMalloc_PageMap1<32 - PAGE_SHIFT> _IdSpanMap; // 对SpanID 和 Span建立映射
#endif
	static PageCache* _pInst;
};